package jk.fox.spring.processor;

import jk.fox.spring.EnableFoxService;
import jk.fox.spring.bootstrap.SpringRpcContext;
import jk.fox.spring.container.SpringRpcContainer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;

import java.util.*;

/**
 * Spring-rpc 核心注册器
 * @see  RpcReferenceBeanPostProcessor
 * @see  RpcServiceBeanPostProcessor
 * @author cuichao
 * @Description: RpcBeanDefinitionRegistrar
 * @Date: create in 2020/5/25 23:35
 */
public class RpcBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar,ResourceLoaderAware,BeanFactoryAware {

    private ResourceLoader resourceLoader;

    private  ConfigurableListableBeanFactory factory;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry){
        //获取包扫描路径
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
        //构建RpcReferenceProcessor注册表
        buildRpcReferenceBeanPostProcessor(registry);
        //构建RpcServiceProcessor注册表
        buildRpcServiceBeanPostProcessor(registry,packagesToScan);
        //构建Spring-Rpc容器
        String container = buildRpcContainerRegister(registry);
        //构建Spring-Rpc上下文对象
        buildRpcContextRegister(registry, container);
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.factory = (ConfigurableListableBeanFactory)beanFactory;
    }


    private String buildRpcReferenceBeanPostProcessor(BeanDefinitionRegistry registry){
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(RpcReferenceBeanPostProcessor.class);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        return  BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
    }



    private String buildRpcServiceBeanPostProcessor(BeanDefinitionRegistry registry, Collection<String> packagesToScan){
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(RpcServiceBeanPostProcessor.class);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        builder.addConstructorArgValue(packagesToScan);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        return  BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
    }

    private String buildRpcContainerRegister(BeanDefinitionRegistry registry){
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(SpringRpcContainer.class);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        return BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
    }

    private String buildRpcContextRegister(BeanDefinitionRegistry registry,String container){
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(SpringRpcContext.class);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        builder.addConstructorArgReference(container);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        return BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
    }

    private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(EnableFoxService.class.getName()));
        String[] basePackages = attributes.getStringArray("basePackages");
        Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
        String[] value = attributes.getStringArray("value");
        // Appends value array attributes
        Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
        packagesToScan.addAll(Arrays.asList(basePackages));
        for (Class<?> basePackageClass : basePackageClasses) {
            packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
        }
        if (packagesToScan.isEmpty()) {
            return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
        }
        return packagesToScan;
    }



}
